package org.luxor.cloud.gateway.component.security;

import org.apache.commons.lang3.StringUtils;
import org.luxor.cloud.gateway.entity.GatewayRouteEntity;
import org.luxor.cloud.gateway.service.DynamicRouteService;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 鉴权管理器，用于判断是否有资源的访问权限
 *
 * @author Mr.Yan  @date: 2020/11/17
 */
public class OAuth2ReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    private DynamicRouteService dynamicRouteService;

    public OAuth2ReactiveAuthorizationManager(DynamicRouteService dynamicRouteService) {
        this.dynamicRouteService = dynamicRouteService;
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        PathContainer path = request.getPath().pathWithinApplication();

        String routePath = path.subPath(2).value();
        String routeVersion = path.subPath(1, 2).value();
        String routeMethod = request.getMethodValue();
        Optional<GatewayRouteEntity> route = dynamicRouteService.getOne(routePath, routeVersion, routeMethod);
        if (route.isPresent() && route.get().getAuthEnabled()) {
            // 收集权限
            List<String> authorities = new ArrayList<>();
            String[] authAuthorities = StringUtils.splitByWholeSeparatorPreserveAllTokens(route.get().getAuthAuthorities(), ",");
            if (authAuthorities != null) {
                authorities.addAll(Arrays.asList(authAuthorities));
            }
            String[] scopes = StringUtils.splitByWholeSeparatorPreserveAllTokens(route.get().getAuthScope(), ",");
            if (scopes != null) {
                for (int i = 0; i < scopes.length; i++) {
                    authorities.add(SecurityConstants.SCOPE_PREFIX + scopes[i]);
                }
            }

            // 投票计数器
            AtomicInteger votes = new AtomicInteger();

            //认证通过且角色匹配的用户可访问当前路径
            return authentication
                    .filter(a -> a.isAuthenticated())
                    .flatMapIterable(a -> a.getAuthorities())
                    .map(g -> g.getAuthority())
                    .any(authority -> {
                        if (authorities.contains(authority)) {
                            //满足一个权限要求投一票
                            votes.getAndIncrement();
                        }
                        return votes.get() == authorities.size() ? true : false;
                    })
                    .map(hasAuthority -> new AuthorizationDecision(hasAuthority))
                    .defaultIfEmpty(new AuthorizationDecision(false));
        }
        return Mono.just(new AuthorizationDecision(true));
    }


}
